package com.springboot.util;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author yiwang
 * @description 数值金额转为中文大写金额
 * @date 2021/12/16
 */
public class MyNumberToChineseUtil {
    protected static final String HanDigiStr[] =
            new String[]{"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};

    protected static final String HanDiviStr[] =
            new String[]{
                    "", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾",
                    "佰", "仟", "万", "拾", "佰", "仟"
            };

    protected static final String KEYS[] =
            new String[]{"SY", "Y", "QW", "BW", "SW", "W", "Q", "B", "S", "G", "J", "F"};

    /**
     * 负责把小数点前面的数转换为大写中文
     *
     * <p>输入字符串必须正整数，只允许前面有空格(必须右对齐)，不允许前面有零
     *
     * @param numberStr
     * @return
     */
    public static String positiveIntegerToHanString(String numberStr) {
        String RMBStr = "";
        boolean lastzero = false;
        boolean hasvalue = false; // 亿、万进位前有数值标记
        int len, n;
        len = numberStr.length();
        if (len > 15) {
            return "金额过大!";
        }

        for (int i = len - 1; i >= 0; i--) {
            if (numberStr.charAt(len - i - 1) == ' ') {
                continue;
            }

            n = numberStr.charAt(len - i - 1) - '0';
            if (n < 0 || n > 9) {
                return "金额含非数字字符!";
            }
            if (n != 0) {
                if (lastzero) {
                    RMBStr += HanDigiStr[0]; // 若干零后若跟非零值，只显示一个零
                }
                // 除了亿万前的零不带到后面
                // if( !( n==1 && (i%4)==1 && (lastzero || i==len-1) ) ) //
                // 如十进位前有零也不发壹音用此行
                //              if (!(n == 1 && (i % 4) == 1 && i == len - 1)) // 十进位处于第一位不发壹音
                RMBStr += HanDigiStr[n];
                RMBStr += HanDiviStr[i]; // 非零值后加进位，个位为空
                hasvalue = true; // 置万进位前有值标记

            } else {
                if ((i % 8) == 0 || ((i % 8) == 4 && hasvalue)) { // 亿万之间必须有非零值方显示万
                    RMBStr += HanDiviStr[i]; // “亿”或“万”
                }
            }
            if (i % 8 == 0) {
                hasvalue = false; // 万进位前有值标记逢亿复位
                lastzero = (n == 0) && (i % 4 != 0);
            }
        }

        if (RMBStr.length() == 0) {
            return HanDigiStr[0]; // 输入空字符或"0"，返回"零"
        }
        return RMBStr;
    }

    /**
     * 输入double型数转换为大写中文
     *
     * @param doubleValue
     * @return 大写中文
     */
    public static String getChineseMoneyStringForDoubleVal(double doubleValue) {

        String SignStr = "";
        String TailStr = "";
        long fraction, integer;
        int jiao, fen;

        if (doubleValue < 0) {
            doubleValue = -doubleValue;
            SignStr = "负";
        }
        if (doubleValue > 99999999999999.999 || doubleValue < -99999999999999.999) {
            return "金额数值位数过大!";
        }
        // 四舍五入到分
        long temp = Math.round(doubleValue * 100);
        integer = temp / 100;
        fraction = temp % 100;
        jiao = (int) fraction / 10;
        fen = (int) fraction % 10;
        if (jiao == 0 && fen == 0) {
            TailStr = "整";
        } else {
            TailStr = HanDigiStr[jiao];
            if (jiao != 0) {
                TailStr += "角";
            }
            if (integer == 0 && jiao == 0) {
                // 零圆后不写零几分
                TailStr = "";
            }

            if (fen != 0) {
                TailStr += HanDigiStr[fen] + "分";
            }
        }

        return (doubleValue >= 1)
                ? (SignStr + positiveIntegerToHanString(String.valueOf(integer)) + "元" + TailStr)
                : TailStr;
    }

    /**
     * 输入double型数转换为大写中文
     *
     * @param doubleValue
     * @param isMoney     是否为金额, 金额加元角分
     * @return 大写中文
     */
    public static String getChineseNumberStringForDoubleVal(double doubleValue, boolean isMoney) {

        String SignStr = "";
        String TailStr = "";
        long fraction, integer;
        int jiao, fen;

        if (doubleValue < 0) {
            doubleValue = -doubleValue;
            SignStr = "负";
        }
        if (doubleValue > 99999999999999.999 || doubleValue < -99999999999999.999) {
            return "金额数值位数过大!";
        }
        // 四舍五入到分
        long temp = Math.round(doubleValue * 100);
        integer = temp / 100;
        fraction = temp % 100;
        jiao = (int) fraction / 10;
        fen = (int) fraction % 10;
        if (jiao == 0 && fen == 0) {
            if (isMoney) {
                TailStr = "整";
            }
        } else {
            if (!isMoney) {
                TailStr += "点";
            }
            TailStr += HanDigiStr[jiao];
            if (jiao != 0 && isMoney) {
                TailStr += "角";
            }
            if (integer == 0 && jiao == 0) {
                // 零圆后不写零几分
                TailStr = "";
            }

            if (fen != 0) {
                TailStr += HanDigiStr[fen];
                if (isMoney) {
                    TailStr += "分";
                }
            }
        }

        String integerStr = positiveIntegerToHanString(String.valueOf(integer));
        if (doubleValue >= 1) {
            return isMoney ? (SignStr + integerStr + "元" + TailStr) : (SignStr + integerStr + TailStr);
        }
        return isMoney ? (SignStr + TailStr) : (SignStr + "零" + TailStr);
    }

    /**
     * 输入BigDecimal型数转换为大写中文
     *
     * <p>精度取决于BigDecimal 的 public double doubleValue() 方法： 是基本收缩转换。 如果此 BigDecimal 的数量太大而不能表示为
     * double，则将其适当地转换为 Double.NEGATIVE_INFINITY 或 Double.POSITIVE_INFINITY。 即使在返回值为有限值的情况下，此转换也可能丢失关于
     * BigDecimal 值精度的信息。
     *
     * @param bigDecimalVal
     * @return 大写中文
     */
    public static String getChineseMoneyStringForBigDecimal(BigDecimal bigDecimalVal) {
        if (ObjectUtil.isEmpty(bigDecimalVal)) {
            return "";
        }
        return getChineseMoneyStringForDoubleVal(bigDecimalVal.doubleValue());
    }

    /**
     * 数字转中文
     *
     * @param value 数字
     */
    public static String getChineseNumberString(BigDecimal value) {
        if (ObjectUtil.isEmpty(value) || BigDecimal.ZERO.equals(value)) {
            return "";
        }
        return getChineseNumberStringForDoubleVal(value.doubleValue(), false);
    }

    /**
     * 金额转中文
     *
     * @param value 数字
     */
    public static String getChineseMoneyString(BigDecimal value) {
        if (ObjectUtil.isEmpty(value) || BigDecimal.ZERO.equals(value)) {
            return "";
        }
        return getChineseNumberStringForDoubleVal(value.doubleValue(), true);
    }

    /**
     * 数字转中文
     *
     * @param value   数字
     * @param isMoney 是否为金额, 金额加元角分
     */
    public static String getChineseNumberString(BigDecimal value, boolean isMoney) {
        if (ObjectUtil.isEmpty(value)) {
            return "";
        }
        return getChineseNumberStringForDoubleVal(value.doubleValue(), isMoney);
    }

    /**
     * 数字转中文map
     *
     * @param value 数字
     */
    public static Map<String, String> getChineseNumberStringMap12(BigDecimal value) {
        return getChineseNumberStringMap(value, "0000000000.00", KEYS);
    }

    /**
     * 数字转中文map
     *
     * @param value 数字
     * @param keys  数字 十亿位 -> 分位 对应的map的key,
     */
    public static Map<String, String> getChineseNumberStringMap(BigDecimal value, String numberPattern, String... keys) {
        if (ObjectUtil.isEmpty(value) || NumberUtil.isLess(value, BigDecimal.ZERO) || ArrayUtil.isEmpty(keys) || StrUtil.isBlank(numberPattern)) {
            return Collections.emptyMap();
        }
        // 金额转大写
        value = value.setScale(2, BigDecimal.ROUND_HALF_UP);
        String valueStr = NumberUtil.decimalFormat(numberPattern, value.doubleValue());
        if (StrUtil.isBlank(valueStr)) {
            return Collections.emptyMap();
        }
        // 去除小数点
        valueStr = valueStr.replace(".", "");
        // 转map
        Map<String, String> map = new LinkedHashMap<>();
        for (int i = 0; i < keys.length; i++) {
            String key = String.valueOf(keys[i]);
            int index = Integer.parseInt(String.valueOf(valueStr.charAt(i)));
            String v = HanDigiStr[index];
            map.put(key, v);
        }
        return map;
    }
}
